using System;
using System.IO;
using System.Diagnostics;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ManagedAPI;

namespace UnitTest
{
	/// <summary>
	/// Summary description for EntityTest
	/// </summary>
	[TestClass]
	public class EntityTest : UnderstandGlobalData
	{

		public EntityTest()
		{
			// This gets called _after_ MyClassInitialize is called
		}

		#region Additional test attributes
		//
		// You can use the following additional attributes as you write your tests:
		//
		// Use ClassInitialize to run code before running the first test in the class
		// This gets called _before_ the constructor
		//[ClassInitialize()]
		//public static void MyClassInitialize(TestContext testContext){}
		//
		// Use ClassCleanup to run code after all tests in a class have run
		//[ClassCleanup()]
		//public static void MyClassCleanup(){}
		//
		// Use TestInitialize to run code before running each test 
		private static bool mOpenResult;
		[TestInitialize()]
		public void MyTestInitialize()
		{
			mOpenResult = Database.Open(UnderstandGlobalData.GetFileName());
			Assert.IsTrue(mOpenResult);
		}
		//
		// Use TestCleanup to run code after each test has run
		[TestCleanup()]
		public void MyTestCleanup()
		{
			Database.Close();
			mOpenResult = false;
		}
		//
		#endregion

		[TestMethod]
		public void EntityTest_Basic()
		{
			if (mOpenResult)
			{
				Entity[] entList = Database.GetFileEntities();
				Assert.AreEqual(10, entList.Length);

				Entity entity = Database.LookupFile("foo_Util.h");
				Assert.AreEqual("foo_Util.h - C Header File", entity.ToString());
				Assert.AreEqual("foo_Util.h", entity.NameShort);
				Assert.AreEqual("foo_Util.h", entity.NameSimple);
				//Assert.AreEqual(38, entity.ID);
				//Assert.AreEqual(null, entity.Kind);
				Assert.AreEqual("C Header File", entity.KindName);
				// Files have no type
				Assert.AreEqual(null, entity.TypeText);
			}
		}

		[TestMethod]
		public void EntityTest_Refs()
		{
			if (mOpenResult)
			{
				Entity ent = Database.LookupFile("classes.h");
				Assert.IsTrue(ent != null);

				Reference[] refs = ent.Refs("define", "class");
				Assert.AreEqual(5, refs.Length);

				Assert.AreEqual("Derived", refs[0].ThisEntity.NameShort);
				Assert.AreEqual("Younger", refs[1].ThisEntity.NameShort);
				Assert.AreEqual("Older",   refs[2].ThisEntity.NameShort);
				Assert.AreEqual("Mommy",   refs[3].ThisEntity.NameShort);
				Assert.AreEqual("giffer",  refs[4].ThisEntity.NameShort);
			}
		}

		[TestMethod]
		public void EntityTest_Types()
		{
			FileEntity classFile = Database.LookupFileEntity("classes.h");
			ClassType[] classes = classFile.GetClassTypeDefinitions();
			ClassType mom = classes[3];
			Assert.AreEqual("Mommy", mom.NameSimple);
			Entity[] mom_fields = mom.GetFields();
			Assert.AreEqual(9, mom_fields.Length);

			Entity field1 = mom_fields[0];
			Assert.AreEqual("my_priv_int", field1.NameSimple);
			Assert.AreEqual("int", field1.TypeText);
			Assert.AreEqual("int", field1.GetCleanType());

			Entity field2 = mom_fields[1];
			Assert.AreEqual("my_static_int", field2.NameSimple);
			Assert.AreEqual("int", field2.TypeText);
			Assert.AreEqual("int", field2.GetCleanType());
			Entity lastOne = mom_fields[8];
			Entity lastOneType = lastOne.GetTypeEntity();
			Assert.AreEqual("OtherBase", lastOneType.NameShort);

			MethodType[] methods = mom.GetMethods();
			MethodType fn_two = methods[2];
			Assert.AreEqual("fn_two", fn_two.NameSimple);
			Assert.AreEqual("char *", fn_two.TypeText);
			Assert.AreEqual("char", fn_two.GetCleanType());
			Assert.AreEqual(null, fn_two.GetTypeEntity());
		}

		[TestMethod]
		public void EntityTest_FileReference()
		{
			if (mOpenResult)
			{
				FileEntity file_Ent = Database.LookupFileEntity("base.h");
				Assert.IsTrue(file_Ent != null);

				ClassType[] classEnts = file_Ent.GetClassTypeDefinitions();
				Assert.AreEqual(1, classEnts.Length);
				Assert.AreEqual("Base", classEnts[0].NameLong);

				Reference fileref = classEnts[0].GetRef();
				String file_name_short = fileref.File.NameShort;
				Assert.AreEqual(@"base.h", file_name_short);


				FileEntity fooUtil = Database.LookupFileEntity("foo_Util.h");
				ClassType[] classes = fooUtil.GetClassTypeDefinitions();
				ClassType memorizer = classes[0];
				Assert.AreEqual("foo::Util::Memorizer", memorizer.NameLong);

				Reference fileRef = memorizer.GetRef();
				String memFileEntName = fileRef.File.NameShort;
				Assert.AreEqual("foo_Util.h", memFileEntName);
			}
		}

		[TestMethod]
		public void EntityTest_LineNumbers()
		{
			if (mOpenResult)
			{
				FileEntity base_File = Database.LookupFileEntity("base.h");
				ClassType[] classes = base_File.GetClassTypeDefinitions();
				ClassType basetype = classes[0];
				Assert.AreEqual(3, basetype.LineDefineStart);
				Assert.AreEqual(11, basetype.LineDefineEnd);

				FileEntity class_File = Database.LookupFileEntity("classes.h");
				classes = class_File.GetClassTypeDefinitions();
				ClassType derived = classes[0];
				Assert.AreEqual(14, derived.LineDefineStart);
				Assert.AreEqual(23, derived.LineDefineEnd);

				// Note:
				// You cannot test the line numbers of namespace definitions. It is inconsistant in the understand 4 c++ database.
				//
				// Note about line numbers for methods.
				// The line number for a method declaration that is defined in another file, will give the line number of the entity where
				// the function is defined, NOT where the function is declared.
				// If the function is unresolved, the line number will be the line number of where the method was declared.
				FileEntity foo_File = Database.LookupFileEntity("foo_Util.h");
				classes = foo_File.GetClassTypeDefinitions();
				ClassType stamper = classes[1];
				Assert.AreEqual("Stamper", stamper.NameSimple);
				// NOTE: Understand does not handle templated classes. Therefore
				// because of the 'template' keyword, it really starts one line before the 'class' keyword.
				Assert.AreEqual(53, stamper.LineDefineStart);
				Assert.AreEqual(60, stamper.LineDefineEnd);
			}
		}
		[TestMethod]
		public void EntityTest_LineNumbers_FunctionDeclarations()
		{
			FileEntity foo_File = Database.LookupFileEntity("foo_Util.h");
			MethodType[] funcs = foo_File.GetFunctionDeclarations();

			// This method has both a declaration, and a definition 
			MethodType funcGetNewMemory = funcs[0];
			Assert.AreEqual("GetNewMemory", funcGetNewMemory.NameSimple);
			Assert.AreEqual(20, funcGetNewMemory.LineDefineStart);
			Assert.AreEqual(26, funcGetNewMemory.LineDefineEnd);
			Assert.AreEqual(21, funcGetNewMemory.LineDeclareStart);
		}
		[TestMethod]
		public void EntityTest_LineNumbers_FunctionDefinitions()
		{
			FileEntity foo_File = Database.LookupFileEntity("foo_Util.h");
			MethodType[] funcs = foo_File.GetFunctionDefinitions();

			// This is an inline method
			MethodType funcGetOldMemory = funcs[0];
			Assert.AreEqual("GetOldMemory", funcGetOldMemory.NameSimple);
			Assert.AreEqual(35, funcGetOldMemory.LineDefineStart);
			Assert.AreEqual(38, funcGetOldMemory.LineDefineEnd);
			Assert.AreEqual(0, funcGetOldMemory.LineDeclareStart);
		}
		[TestMethod]
		public void EntityTest_LineNumbers_UnresolvedMethods()
		{
			FileEntity foo_File = Database.LookupFileEntity("foo_Util.h");
			MethodType[] funcs = foo_File.GetFunctionDeclarations();

			// This is an inline method
			MethodType funcUnresolved = funcs[1];
			Assert.AreEqual("MyUnresolvedMethod", funcUnresolved.NameSimple);
			Assert.AreEqual(0, funcUnresolved.LineDefineStart);
			Assert.AreEqual(0, funcUnresolved.LineDefineEnd);
			Assert.AreEqual(23, funcUnresolved.LineDeclareStart);
		}
		[TestMethod]
		public void EntityTest_Comments()
		{
			FileEntity file_Ent = Database.LookupFileEntity("classes.h");
			ClassType[] classes = file_Ent.GetClassTypeDefinitions();
			ClassType derived = classes[0];

			String expected = "classes are below\nThis is a derived class.\n\tIt inherits from Base of course.";
			Assert.AreEqual(expected, derived.Comments.DefineBefore);

			expected = "/*\tThis is a derived class.\n\tIt inherits from Base of course.\n*/";
			
			ReadOnlyCollection<String> rawCommentsList = derived.Comments.DefineBeforeRaw;
			Assert.AreEqual(expected, rawCommentsList[1]);
			Assert.IsTrue(String.IsNullOrEmpty(derived.Comments.DefineAfter));
			//--------------------------------------------------
			// Test comments on methods
			MethodType[] methods = derived.GetMethods();
			MethodType fn_two = methods[2];

			Assert.AreEqual("some virtual methods", fn_two.Comments.DeclareBefore);
			Assert.AreEqual("different from base signature", fn_two.Comments.DeclareAfter);
		}
		[TestMethod]
		public void EntityTest_Comments_FunctionDeclarations()
		{
			// -------------------------------------------------
			// Test comments on global functions
			FileEntity fooUtilEnt = Database.LookupFileEntity("foo_Util.h");
			MethodType[] funcs = fooUtilEnt.GetFunctionDeclarations();

			MethodType getNewMemoryMethod = funcs[0];
			Assert.AreEqual("GetNewMemory", getNewMemoryMethod.NameSimple);
			Assert.AreEqual("Function declaration. defined in foo_Util.cpp", getNewMemoryMethod.Comments.DeclareBefore);
			Assert.AreEqual("// Function declaration. defined in foo_Util.cpp", getNewMemoryMethod.Comments.DeclareBeforeRaw[0]);

			MethodType unresolvedMethod = funcs[1];
			Assert.AreEqual("MyUnresolvedMethod", unresolvedMethod.NameSimple);
			Assert.AreEqual("Function delcaration. No definition anywhere.", unresolvedMethod.Comments.DeclareBefore);
			Assert.AreEqual("// Function delcaration. No definition anywhere.", unresolvedMethod.Comments.DeclareBeforeRaw[0]);
		}
		[TestMethod]
		public void EntityTest_Comments_FunctionDefinitions()
		{
			// -------------------------------------------------
			// Test comments on global functions
			FileEntity fooUtilEnt = Database.LookupFileEntity("foo_Util.h");
			MethodType[] funcs = fooUtilEnt.GetFunctionDefinitions();

			MethodType getOldMemoryMethod = funcs[0];
			Assert.AreEqual("GetOldMemory", getOldMemoryMethod.NameSimple);
			Assert.AreEqual("Function definition. inline", getOldMemoryMethod.Comments.DefineBefore);
			Assert.AreEqual("", getOldMemoryMethod.Comments.DeclareBefore);
			Assert.AreEqual("// Function definition. inline", getOldMemoryMethod.Comments.DefineBeforeRaw[0]);
			Assert.AreEqual(0, getOldMemoryMethod.Comments.DeclareBeforeRaw.Count);

			MethodType makeMemoryMethod = funcs[1];
			Assert.AreEqual("MakeMemory", makeMemoryMethod.NameSimple);
			Assert.AreEqual("Another inline method", makeMemoryMethod.Comments.DefineBefore);
			Assert.AreEqual("", makeMemoryMethod.Comments.DeclareBefore);
			Assert.AreEqual("// Another inline method", makeMemoryMethod.Comments.DefineBeforeRaw[0]);
			Assert.AreEqual(0, makeMemoryMethod.Comments.DeclareBeforeRaw.Count);
		}
		[TestMethod]
		public void EntityTest_Comments_Typedefs()
		{
			FileEntity baseEnt = Database.LookupFileEntity("base.h");
			Reference[] typedefs = baseEnt.Refs("define", "typedef");
			Assert.AreEqual(3, typedefs.Length);

			Reference typedefBasePtr = typedefs[0];
			Assert.AreEqual(14, typedefBasePtr.Line);
			Assert.AreEqual("A Base Pointer", typedefBasePtr.ThisEntity.Comments.DefineBefore);
			Assert.AreEqual("// A Base Pointer", typedefBasePtr.ThisEntity.Comments.DefineBeforeRaw[0]);
		}
		[TestMethod]
		public void EntityTest_Comments_Enums()
		{
			FileEntity baseEnt = Database.LookupFileEntity("base.h");
			Entity[] enums = baseEnt.GetEnumDefintions();

			Entity EnumDaysofWeek = enums[0];
			Assert.AreEqual("Make sure understand correctly picks up these comments.", EnumDaysofWeek.Comments.DefineBefore);
			Assert.AreEqual("", EnumDaysofWeek.Comments.DeclareBefore);
			Assert.AreEqual("// Make sure understand correctly picks up these comments.", EnumDaysofWeek.Comments.DefineBeforeRaw[0]);
			Assert.AreEqual(0, EnumDaysofWeek.Comments.DeclareBeforeRaw.Count);
		}
	}
}
